﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using HashFoo.Rest.Common;

namespace HashFoo.Rest.Server
{
    /// <summary>
    /// Handles serializing a <see cref="ResourceActionResult"/> to the response stream.
    /// </summary>
    public class ResourceSerializerFilter : IResultFilter
    {
        private static readonly FuncResourceSerializer DefaultSerializer =
            new FuncResourceSerializer
                {
                    Reader = (s, t) => s,
                    Writer = obj => obj.ToString()
                };

        private readonly IEnumerable<IResourceSerializer> _resourceSerializers;

        /// <summary>
        /// Creates a <see cref="ResourceSerializerFilter"/> with a set of <see cref="IResourceSerializer"/>s.
        /// </summary>
        /// <param name="resourceSerializers"></param>
        public ResourceSerializerFilter(IEnumerable<IResourceSerializer> resourceSerializers)
        {
            _resourceSerializers = resourceSerializers;
        }

        /// <summary>
        /// Called before an action result executes.  This where the serialization and response writing take place.
        /// </summary>
        /// <param name="filterContext">The filter context.</param>
        public void OnResultExecuting(ResultExecutingContext filterContext)
        {
            var resourceResult = filterContext.Result as ResourceActionResult;
            if (resourceResult == null) return;

            // if we made it here, we're going to handle this ourselves

            // if no accepttype, you're going to get a crappy ToString
            if (filterContext.HttpContext.Request.AcceptTypes == null ||
                filterContext.HttpContext.Request.AcceptTypes.Length == 0)
            {
                Serialize(resourceResult);
                return;
            }

            // get the first (or default) serializer that handles the accepttype of the request.
            var serializer =
                _resourceSerializers.FirstOrDefault(
                    rexSerializer => rexSerializer.AcceptMimeTypes.Any(
                        m => filterContext.HttpContext.Request.AcceptTypes.Any(x => x == m.ToLowerInvariant())));

            // if no serializer for request type, poop, you're getting ToString, else, we'll serialize for you.
            // todo: return error if unknown MIME
            if (serializer == null) Serialize(resourceResult);
            else Serialize(resourceResult, serializer);
        }

        // Func<T, string> serializer = null, string contentType = "text/text"
        private static void Serialize(ResourceActionResult actionResult, IResourceSerializer serializer = null)
        {
            serializer = serializer ?? DefaultSerializer;

            actionResult.ContentType = serializer.ContentMimeType;
            actionResult.Content = serializer.Write(actionResult.Resource);
        }

        /// <summary>
        /// Called after an action result executes.
        /// </summary>
        /// <param name="filterContext">The filter context.</param>
        /// <remarks>
        /// This doesn't do anything.  Serialization and response writing takes place when the action is executed.
        /// </remarks>
        void IResultFilter.OnResultExecuted(ResultExecutedContext filterContext)
        {
        }
    }
}